package com.kirito.mvcframework.v3;

import com.kirito.mvcframework.Annotation.*;
import com.kirito.mvcframework.strategy.CovertEnum;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.logging.Handler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName DispatcherServlet
 * @Description: TODO
 * @Author kirito
 * @Date 2021/1/7
 * @Version V1.0
 **/
public class DispatcherServlet extends HttpServlet {
    //保存配置文件
    private Properties contextConfig = new Properties();
    private List<String> classNames = new ArrayList<String>();
    private Map<String,Object> ioc = new HashMap<String, Object>();
    //private Map<String,Method> handlerMapping = new HashMap<String, Method>();
    // 阅读源码发现 在Spring源码中 handlerMapping 是一个list集合  list中的元素是一个自定义的类型
    private List<Handler> handlerMapping = new ArrayList<Handler>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            resp.getWriter().write(Arrays.toString(e.getStackTrace()));
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp)  {
        resp.setContentType("text/html;charset=UTF-8");
        try {
            //1 获取请求的url
            Handler handler = null;

                handler = getHandler(req);

            if (handler == null){
                resp.getWriter().write("404 not found");
                return;
            }
            //获取方法的参数列表
            Class<?> [] paramTypes = handler.method.getParameterTypes();
            //保存所有需要自动赋值的参数值
            Object [] paramValues = new Object[paramTypes.length];
            //获取请求参数
            Map<String,String[]> params = req.getParameterMap();
            for (Map.Entry<String, String[]> parm : params.entrySet()) {
                String value = Arrays.toString(parm.getValue()).replaceAll("\\[|\\]","").replaceAll("\\s",",");
                if(!handler.paramIndexMapping.containsKey(parm.getKey())){continue;}
                int index = handler.paramIndexMapping.get(parm.getKey());
                paramValues[index] = convert(paramTypes[index],value);
            }
            //设置方法中的request和response对象
            int rqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[rqIndex] = req;
            Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex] = resp ;

            handler.method.invoke(handler.controller,paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object convert(Class<?> type, String value) {
        if (String.class != type){
            String paramsType = type.toString();
            String typeName = paramsType.substring(paramsType.lastIndexOf(".") + 1);

            return CovertEnum.valueOf(typeName).getPc().convert(value);
        }
        return  value ;
    }

    private Handler getHandler(HttpServletRequest req) throws Exception {
        if (handlerMapping.isEmpty()){return  null;}
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");
        for (Handler handler : handlerMapping) {
            try {
                //正则匹配
                Matcher matcher = handler.pattern.matcher(url);
                if (!matcher.matches()) {
                    continue;
                }
                return  handler;
            }catch (Exception e){
                throw e;
            }
        }
        return  null ;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1,加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2,扫描相关的类
        doScanner(this.contextConfig.getProperty("scanPackage"));
        //3,初始化扫描的类
        doInstance();
        //4,依赖注入
        doAutowired();
        //5,初始化handlerMapping
        doHandlerMapping();
    }

    private void doHandlerMapping() {
        if (ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            //是否有controller注解
            if (!clazz.isAnnotationPresent(Controller.class)){continue;}
            String url = "" ;
            //获取controller上的Url
            if (clazz.isAnnotationPresent(RequestMapping.class)){
                RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                url = requestMapping.value();
            }
            //获取 Method 的 url 配置
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(RequestMapping.class)) {continue;}
                //映射url
                RequestMapping mapping = method.getAnnotation(RequestMapping.class);
                String regex = ("/"+url+mapping.value()).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                handlerMapping.add(new Handler(pattern,entry.getValue(),method));
                System.out.println("mapping " + regex + "," + method);
            }
        }

    }

    private void doAutowired() {
        if (ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {

                try {
                    if (!field.isAnnotationPresent(Autowired.class)) { continue; }
                    String beanName = field.getAnnotation(Autowired.class).value();
                    if ("".equals(beanName)){
                        //获取接口的全类名
                        beanName = field.getType().getName();
                    }
                    // 在扫描的类名容器 匹配实现了该接口的
                    for (String className : classNames) {
                        Class<?> clazz = Class.forName(className);
                        if (clazz.isInterface()){continue;}
                        for (Class<?> anInterface : clazz.getInterfaces()) {
                           if ( anInterface.getName().equals(beanName) ){
                               beanName = className;
                               break;
                           }
                        }
                    }
                    field.setAccessible(true);
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("自动注入完毕");
    }

    private void doInstance() {
        if (classNames.isEmpty()){return;}
        for (String className: classNames) {
            try {
                Class<?> clazz = Class.forName(className);

                if (clazz.isAnnotationPresent(Controller.class)){
                    //@Controller
                    //这里偷个懒 不做标准化
                    ioc.put(clazz.getName(),clazz.newInstance());
                }else if(clazz.isAnnotationPresent(Service.class)){
                    //@Service
                    Service service = clazz.getAnnotation(Service.class);
                    String beaName = service.value();
                    if ("".equals(beaName)){ beaName = clazz.getName(); }
                    ioc.put(beaName,clazz.newInstance());
                }else{
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("ioc 初始化完毕");
    }

    private void doScanner(String scanPackage) {
        //路径转换
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                if (!file.getName().endsWith(".class")){continue;}
                //保存全类名
                String beanName = (scanPackage + "."+file.getName().replace(".class",""));
                classNames.add(beanName);
            }
        }
    }

    private void doLoadConfig(String contextConfigLocation) {
        InputStream is = null;
        is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            this.contextConfig.load(is);
            System.out.println("配置文件已加载");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (is!= null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class Handler{
        protected Object controller ; //保存对应方法的实例
        protected Method method ;  // 保存方法的映射
        protected Pattern pattern; //正则
        protected Map<String,Integer> paramIndexMapping ; //参数顺序

        protected Handler(Pattern pattern ,Object controller , Method method){
            this.pattern = pattern ;
            this.controller = controller ;
            this.method = method ;
            paramIndexMapping = new HashMap<String, Integer>();
            putParamIndexMapping(method);
        }

        private void putParamIndexMapping(Method method) {
            //获取注解上的参数
            Annotation[][] annotations = method.getParameterAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                for (Annotation annotation : annotations[i]) {
                    if (annotation instanceof RequestParam){
                        String value = ((RequestParam) annotation).value();
                        if (!"".equals(value)){
                            paramIndexMapping.put(value,i);
                        }
                    }
                }
            }
           //提取方法中的request和response参数
            Class<?>[] types = method.getParameterTypes();
            for (int i = 0; i < types.length; i++) {
                Class<?> type = types[i];
                if (type == HttpServletRequest.class || type == HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(),i);
                }
            }
        }

    }
}
